package fileio;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;

public class FileIOTest {
	private static final Logger LOGGER = LoggerFactory.getLogger(FileIOTest.class);

	private static final String TEST_PATH = "D:\\test\\";

	@Test
	public void testCharset() throws IOException {
		Path path = Paths.get(TEST_PATH + "test.txt");
		Files.deleteIfExists(path);
		Files.write(path, "你好".getBytes(Charset.forName("GBK")));
		LOGGER.info(new String(Files.readAllBytes(path), Charset.forName("UTF-8")));
	}

	@Test
	public void testInputStreamAndReader() {
		char[] buf = new char[10];
		StringBuilder content = new StringBuilder();
		try (FileInputStream fileInputStream = new FileInputStream(TEST_PATH + "test.txt")) {
			InputStreamReader reader = new InputStreamReader(fileInputStream, Charset.forName("GBK"));
			int len;
			while ((len = reader.read(buf)) != -1) {
				content.append(buf, 0, len);
			}
			LOGGER.info(content.toString());
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Test
	public void perByteOperation() {
		LOGGER.info("开始单字节单字节复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			int i;
			while ((i = fis.read()) != -1) {
				fos.write(i);
			}
		} catch (Exception e) {
			LOGGER.error("单字节单字节复制文件异常", e);
		}
		LOGGER.info("结束单字节单字节复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

	@Test
	public void bufferOperationWith100() {
		LOGGER.info("开始带100字节缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			byte[] buf = new byte[100];
			int len;
			while ((len = fis.read(buf)) != -1) {
				fos.write(buf, 0, len);
			}
		} catch (Exception e) {
			LOGGER.error("带100字节缓冲区复制文件", e);
		}
		LOGGER.info("开始带100字节缓冲区复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

	@Test
	public void bufferStreamByteOperation() {
		LOGGER.info("开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(TEST_PATH + "test1.txt"));
		     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_PATH + "dest.txt"))) {
			int i;
			while ((i = bis.read()) != -1) {
				bos.write(i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

	@Test
	public void bufferStreamBufferOperation() {
		LOGGER.info("bufferStreamBufferOperation开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(TEST_PATH + "test1.txt"));
		     BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(TEST_PATH + "dest.txt"))) {
			byte[] buf = new byte[8196];
			int i;
			while ((i = bis.read(buf)) != -1) {
				bos.write(buf, 0, i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

	@Test
	public void streamBufferOperation() {
		LOGGER.info("streamBufferOperation开始带缓冲区复制文件...");
		long start = System.currentTimeMillis();
		try (FileInputStream fis = new FileInputStream(TEST_PATH + "test1.txt");
		     FileOutputStream fos = new FileOutputStream(TEST_PATH + "dest.txt")) {
			byte[] buf = new byte[8196];
			int i;
			while ((i = fis.read(buf)) != -1) {
				fos.write(buf, 0, i);
			}
		} catch (Exception e) {
			LOGGER.error("带缓冲区复制文件文件异常", e);
		}
		LOGGER.info("开始带缓冲区复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

	/**
	 * @Description 采用的是DMA（直接内存访问）也就是数据从磁盘经过总线直接发送到目标
	 * 文件，无需经过内存和 CPU 进行数据中转：
	 * @author chenwb
	 * @date 2020/9/8 9:21
	 * @param
	 * @return
	 */
	@Test
	public void fileChannelOperation() throws IOException {
		LOGGER.info("fileChannelOperation开始复制文件...");
		long start = System.currentTimeMillis();
		FileChannel in = FileChannel.open(Paths.get(TEST_PATH + "test1.txt"), StandardOpenOption.READ);
		FileChannel out = FileChannel.open(Paths.get(TEST_PATH + "dest.txt"), StandardOpenOption.CREATE,
				StandardOpenOption.WRITE);
		in.transferTo(0, in.size(), out);
		LOGGER.info("结束复制文件...，耗时：{}ms", System.currentTimeMillis() - start);
	}

}
